package controllers;

import static play.libs.Json.toJson;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import models.FileInfo;
import models.Profile;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.RESTUtility;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.session.AccessTokenPair;
import com.dropbox.client2.session.AppKeyPair;
import com.dropbox.client2.session.RequestTokenPair;
import com.dropbox.client2.session.Session.AccessType;
import com.dropbox.client2.session.WebAuthSession;
import com.dropbox.client2.session.WebAuthSession.WebAuthInfo;

import play.mvc.*;

public class Dropbox extends Controller {
  
	private static final String APP_KEY = "ow4tlb9w8pr4g78";
    private static final String APP_SECRET = "x6p1q0p5vbofl6b";
    private static final AccessType ACCESS_TYPE = AccessType.DROPBOX;
    
    private static String HOST = "http://localhost:9000";
    private static String CALL_BACK_URL = HOST + "/dropbox/return";//HAS TO include http://
    
    private static ConcurrentHashMap <String, RequestTokenPair> pairs = new ConcurrentHashMap<String, RequestTokenPair>();
    
    ///////////////////////////////////////////////////////////////////////////////////
    // Initialization
    ///////////////////////////////////////////////////////////////////////////////////
	static {
		try{
			Properties prop = new Properties();
			prop.load(new FileInputStream("config.properties"));
		
			String HOST = prop.getProperty("filyou.host");
			String CALL_BACK_URL = HOST + "/dropbox/return";
		
			System.out.println("Properties ok. CALL_BACK_URL: " + CALL_BACK_URL);
		} catch (Exception e){
			System.out.println("Error getting properties: " + e.getLocalizedMessage());
		}
	}
    
    ///////////////////////////////////////////////////////////////////////////////////
    //Authentication methods
    ///////////////////////////////////////////////////////////////////////////////////
	public static Result link() {
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE);
		
		try {
			WebAuthInfo authInfo = session.getAuthInfo(CALL_BACK_URL);

			RequestTokenPair pair = authInfo.requestTokenPair;
	        String url = authInfo.url;
	        
	        // Pair.key is returned as oauth_token in callback call
	        pairs.put(pair.key, pair);
	 
	        return (redirect(url));
	        
		} catch (DropboxException e) {
			e.printStackTrace();
			return internalServerError("Error trying to get Dropbox Authentication");
		}		
	}
	
	public static Result callBackAuth(String uid, String oauth_token){
		try {
			AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
			WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE);
			
			// Rebuild session
			session.retrieveWebAccessToken(pairs.get(oauth_token));
			
			// Get token
	        AccessTokenPair token = session.getAccessTokenPair();
	        saveToken(token);
		        
		} catch (DropboxException e) {
			e.printStackTrace();
			return internalServerError("Error trying to get Dropbox Authentication - part 2");
		}
		return ok(toJson("Dropbox account successfully linked!"));
	}
	
	private static void saveToken(AccessTokenPair token) {
		Profile p;
		try {
			p = Profile.all().get(0);
		} catch (Exception e){ //#Probably null pointer, if no profile created yet
			p = new Profile();
			p.save();
		}
		p.dpkey = token.key;
		p.dpSecret = token.secret;
		p.update();
		System.out.println("Token saved");
	}
	
	///////////////////////////////////////////////////////////////////////////////////
	//Business methods
	///////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Get Metadata from ROOT
	 * @param key
	 * @param secret
	 * @throws Exception
	 */
	public static List<FileInfo> getMetadata(String key, String secret, String filePath) throws Exception{
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		List<FileInfo> fileChd = null;
		
		if (filePath == null || filePath.isEmpty()){
			filePath = "";
		}
		
		//Max 1000 files. If = 0 then 25000 (dropbox default)
		int maxFiles = 1000;
		try {
			DropboxAPI.Entry fileEntry = mDBApi.metadata(filePath, maxFiles, null, true, null);
			
			if (fileEntry.isDir){
				List<DropboxAPI.Entry> children = fileEntry.contents;
				fileChd = new ArrayList<FileInfo>(children.size());
				
				for (DropboxAPI.Entry entry : children){
					fileChd.add(
							new FileInfo(entry.fileName(), entry.size, entry.isDir, entry.path, RESTUtility.parseDate(entry.modified))
							);
				}
			} else {
				fileChd = new ArrayList<FileInfo>(1);
				fileChd.add(new FileInfo(fileEntry.fileName(), fileEntry.size, fileEntry.isDir, fileEntry.path, RESTUtility.parseDate(fileEntry.modified)));
			}
			return fileChd;		
		} catch (DropboxException e) {
			throw new Exception("Error getting files data:" + e.getMessage(), e);
		}
	}

	public static List<FileInfo> search(String key, String secret, String query, String path) throws Exception{
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		List<FileInfo> fileChd = new ArrayList<FileInfo>();
		
		if (query == null || query.isEmpty()){
			query = "";
		}
		
		if (path == null || path.isEmpty()){
			path = "";
		}
		
		//Max 1000 files. If = 0 then 25000 (dropbox default)
		int maxFiles = 1000;
		try {
			for (DropboxAPI.Entry entry : mDBApi.search(path, query, maxFiles, false)){
				fileChd.add(
						new FileInfo(entry.fileName(), entry.size, entry.isDir, entry.path, RESTUtility.parseDate(entry.modified))
						);
			}
			return fileChd;		
		} catch (DropboxException e) {
			throw new Exception("Error getting files data:" + e.getMessage(), e);
		}
	}
		
	///////////////////////////////////////////////////////////////////////////////////
	//Unused methods. Preserved for future possible use
	///////////////////////////////////////////////////////////////////////////////////	
	/*
	public static String upload(String key, String secret, String filename, File file) throws Exception{
		try {
			DropboxAPI<WebAuthSession> mDBApi;
			
		    FileInputStream fis = null;			
		    fis = new FileInputStream(file);
		    
			AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
			
			AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
			
			WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
			
			mDBApi = new DropboxAPI<WebAuthSession>(session);
			
	        System.out.println("Uploading file...");
	        DropboxAPI.Entry newEntry = mDBApi.putFile("/files/entity/" + filename, fis, file.length(), null, null);
	        System.out.println("Uploaded!");
	        return newEntry.path;
		} catch (Exception e){
			throw new Exception (e);
		}
		
	}

	public static boolean delete(String key, String secret, String path) throws Exception{
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		mDBApi.delete(path);
		
		return false;
	}

	public static InputStream getFile(String key, String secret, String path) {
		DropboxAPI<WebAuthSession> mDBApi;
		AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
		AccessTokenPair accessTokenPair = new AccessTokenPair(key, secret);
		WebAuthSession session = new WebAuthSession(appKeys, ACCESS_TYPE, accessTokenPair);
		mDBApi = new DropboxAPI<WebAuthSession>(session);
		
		try {
			return mDBApi.getFileStream(path, null);
		} catch (Exception e){
			return null;
		}
		
	} */
}